#include <stdlib.h>
#include "adders.h"
#include "gates.h"
#include "register.h"

/*
 * Initializes a new instance of a full adder.
 */
FullAdderPtr fullAdderConstruct(void)
{
	FullAdderPtr adder = (FullAdderPtr)malloc(sizeof(FullAdder));
	
	adder->a =
	adder->b =
	adder->carry_in =
	adder->result =
	adder->carry_out =
		FALSE;

	return adder;
}

/*
 * Sets the input bits for the adder.
 */
void fullAdderSetInputs(FullAdderPtr adder, Bit a, Bit b, Bit carry_in)
{
	adder->a = maskBit(a);
	adder->b = maskBit(b);
	adder->carry_in = maskBit(carry_in);
}

/*
 * Performs the addition operation. This method must be called before fullAdderGetResult or
 * fullAdderGetCarryOut.
 */
void fullAdderAdd(FullAdderPtr adder)
{
	Bit aXorB = xor(adder->a, adder->b);
	
	adder->result = xor(aXorB, adder->carry_in);
	adder->carry_out = or(and(adder->a, adder->b), and(adder->carry_in, aXorB));
}

/*
 * Gets the result of the addition operation. The fullAdderAdd method must be called before this
 * method.
 */
Bit fullAdderGetResult(FullAdderPtr adder)
{
	return adder->result;
}

/*
 * Gets the carry-out bit of the the addition operation. The fullAdderAdd method must be called
 * before this method.
 */
Bit fullAdderGetCarryOut(FullAdderPtr adder)
{
	return adder->carry_out;
}

/*
 * Frees an instance of a full adder.
 */
void fullAdderDestruct(FullAdderPtr adder)
{
	free(adder);
}

/*
 * Initializes a new instance of a ripple carry adder.
 */
RippleCarryAdderPtr rippleCarryAdderConstruct(void)
{
	RippleCarryAdderPtr adder = (RippleCarryAdderPtr)malloc(sizeof(RippleCarryAdder));
	int i;
	
	for (i = 0; i < REGISTER_BITS; i++)
		adder->adders[i] = fullAdderConstruct();
	
	adder->a =
	adder->b =
	adder->result =
	adder->flags =
		0;

	return adder;
}

/*
 * Sets the input registers for the adder.
 */
void rippleCarryAdderSetRegisters(RippleCarryAdderPtr adder, Register a, Register b)
{
	adder->a = a;
	adder->b = b;
}

/*
 * Performs the addition operation. This method must be called before rippleCarryAdderGetResult or
 * rippleCarryAdderGetFlags.
 */
void rippleCarryAdderAdd(RippleCarryAdderPtr adder)
{
	Bit carry = FALSE;
	int nonZeroRegisters = 0;
	int i;
	
	adder->result = 0;
	
	for (i = 0; i < REGISTER_BITS; i++)
	{
		fullAdderSetInputs
		(
			adder->adders[i],
			maskBit(adder->a >> i),
			maskBit(adder->b >> i),
			carry
		);
		
		fullAdderAdd(adder->adders[i]);
		
		if (fullAdderGetResult(adder->adders[i]) == TRUE)
		{
			adder->result |= (1 << i);
			nonZeroRegisters++;
		}
		
		carry = fullAdderGetCarryOut(adder->adders[i]);
	}
	
	FullAdderPtr signAdder = adder->adders[REGISTER_BITS - 1];
	adder->flags = 0;
	
	if
	(
		((signAdder->a) == (signAdder->b)) &&
		((signAdder->a) != (signAdder->result))
	)
		adder->flags |= 0x1;
	
	if (carry)
		adder->flags |= 0x2;
	
	if (nonZeroRegisters == 0)
		adder->flags |= 0x4;
	
	if (signAdder->result == TRUE)
		adder->flags |= 0x8;
}

/*
 * Gets the register result of the addition operation. The rippleCarryAdderAdd method must be
 * called before this method.
 */
Register rippleCarryAdderGetResult(RippleCarryAdderPtr adder)
{
	return adder->result;
}

/*
 * Gets the flags from the addition operation. The rippleCarryAdderAdd method must be called before
 * this method.
 */
unsigned char rippleCarryAdderGetFlags(RippleCarryAdderPtr adder)
{
	return adder->flags;
}

/*
 * Frees an instance of a ripple carry adder.
 */
void rippleCarryAdderDestruct(RippleCarryAdderPtr adder)
{
	int i;
	
	for (i = 0; i < REGISTER_BITS; i++)
		fullAdderDestruct(adder->adders[i]);
	
	free(adder);
}
